#include "redfileio.h"

extern "C"
void
load_module ()
{
  addToSystem ("ls", dListDir);
  addToSystem ("mkdir", dMakeDir);
  addToSystem ("rmdir", dRemoveDir);
  addToSystem ("temp-file", dTempFile);
  addToSystem ("temp-name", dTempName);
  //addToSystem ("permissions", dFilePerm);
  addToSystem ("is-dir", dIsDir);
  addToSystem ("is-file", dIsFile);
  addToSystem ("is-eof", dIsEOF);
  addToSystem ("file-size", dFileSize);
  addToSystem ("path-exists", dFileExists);
  addToSystem ("filep", dTypeFile);
  addToSystem ("open", dOpenFile);
  addToSystem ("close", dCloseFile);
  addToSystem ("read", dReadFromFile);
  addToSystem ("readln", dReadLineFromFile);
  addToSystem ("readc", dReadCharFromFile);
}

dFile::dFile ()
{
  type = 'z';
  typeStr = "FILE";
  file = 0;
}

dFile::dFile (const string filename, const char *open_mode)
{
  type = 'z';
  typeStr = "FILE";
  this->filename = filename;
  file = fopen (filename.c_str (), open_mode);
}

string
dFile::toString ()
{
  string out = "File ";
  if (!filename.empty ())
    return filename;
  return "nil";
}

dFile::~dFile ()
{
  delete file;
}

dObject *
dTypeFile (dList *args)
{
  if (args == 0)
    return new dError ("filep requires at least one parameter");
  if ((*args)[0]->type == 'z' && (*args)[0]->typeStr == "FILE")
    return T;
  return nil;
}

dObject *
dOpenFile (dList *args)
{
  if (args == 0 || args->size > 2 || (*args)[0]->type != 's')
    return new dError ("open requires two string parameters");
  string filename = ((dString*)(*args)[0])->value;
  if (isDirectory (filename) == -1)
    return new dError ("Could not open file");
  string open_mode = "r";
  if ((*args)[1] != nil)
    if ((*args)[1]->type == 's')
      open_mode = ((dString*)(*args)[1])->value;
    else
      return new dError ("open requires both parameters to be strings");
  return new dFile ((*args)[0]->toString (), open_mode.c_str ());
}

dObject *
dReadFromFile (dList *args)
{
  if (args == 0 || args->size > 2 || (*args)[0]->typeStr != "FILE")
    return new dError ("read requires a File object parameter and maybe an integer");
  dFile *temp = (dFile*)(*args)[0];
  int buffer_length = 256;
  if ((*args)[1] != nil)
    if ((*args)[1]->type == 'i')
      buffer_length = toInt ((dNumber*)(*args)[1])+1;
    else
      return new dError ("read requires the 2nd argument to be an integer");
  if (temp->file != 0) // There's a file open
    if (!feof (temp->file))
      {
	char blah_ptr[buffer_length];
	string blah = "";
	if (fgets (blah_ptr, buffer_length, temp->file) == NULL)
	  return nil;
	blah += blah_ptr;
	return new dString (blah);
      }
  return nil;
}

dObject *
dReadLineFromFile (dList *args)
{
  if (args == 0 || args->size > 1 || (*args)[0]->typeStr != "FILE")
    return new dError ("readln requires a File object parameter");
  dFile *temp = (dFile*)(*args)[0];
  if (temp->file != 0) // There's a file open
    if (!feof (temp->file))
      {
	char blah_ptr[256];
	string blah = "";
	int c = fgetc (temp->file);
	while (c != '\n' && c != EOF)
	  {
	    blah += c;
	    c = fgetc (temp->file);
	  }
	return new dString (blah);
      }
  return nil;
}

dObject *
dReadCharFromFile (dList *args)
{
  if (args == 0 || args->size != 1 || (*args)[0]->typeStr != "FILE")
    return new dError ("readc requires a File object parameter");
  dFile *temp = (dFile*)(*args)[0];
  if (temp->file != 0) // There's a file open
    if (!feof (temp->file))
      {
	string blah = "";
	blah += fgetc (temp->file);
	return new dString (blah);
      }
  return nil;
}

dObject *
dCloseFile (dList *args)
{
  if (args == 0 || args->size > 1 || (*args)[0]->typeStr != "FILE")
    return new dError ("close requires a File object parameter");
  dFile *temp = (dFile*)(*args)[0];
  if (temp->file != 0)
    fclose (temp->file);
  else
    return nil;
  return T;
}

dObject *
dIsEOF (dList *args)
{
  if (args == 0 || args->size > 1 || (*args)[0]->typeStr != "FILE")
    return new dError ("is-eof requires a File object parameter");
  dFile *temp = (dFile*)(*args)[0];
  if (temp->file != 0)
    {
      if (feof (temp->file))
	return T;
    }
  else
    return T;
  return nil;
}

dObject *
dListDir (dList *args)
{
  if (args == 0 || (*args)[0]->type == 's')
    {
      FILE *pipeIn;
      string temp, command;
      char buffer[256];
      dList *in = new dList ();
      #ifdef TARGET_OS_WINDOWS
      command = "dir ";
      #else
      command = "ls -1 ";
      #endif
      if (args != 0 && args->size == 1)
	command += dProcessString (((dString*)(*args)[0])->value);
      pipeIn = popen (command.c_str (), "r");
      while (!feof (pipeIn))
	{
	  fgets (buffer, 256, pipeIn);
	  temp = buffer;
	  temp = temp.substr (0, temp.length ()-1) + "\0";
	  in->append (new dString (temp));
	}
      pclose (pipeIn);
      command.clear ();
      return in;
    }
  return new dError ("list-dir requires 0 arguments or 1 string");
}

dObject *
dMakeDir (dList *args)
{
  if (args == 0 || args->size > 1)
    return new dError ("mk-dir requires a string argument");
  if ((*args)[0]->type == 's')
    {
      FILE *pipeIn;
      string command;
      char buffer[256];
      command = ((dString*)(*args)[0])->toString ();
      if (!FileExists (command)) // Create a non-existing directory
	command = "mkdir " + command;
      else
	return nil;
      pipeIn = popen (command.c_str (), "r");
      pclose (pipeIn);
      command.clear ();
    }
  return T;
}

dObject *
dRemoveDir (dList *args)
{
  if (args == 0 || args->size > 1)
    return new dError ("rm-dir requires a string argument");
  if ((*args)[0]->type == 's')
    {
      FILE *pipeIn;
      string command;
      char buffer[256];
      command = ((dString*)(*args)[0])->toString ();
      if (FileExists (command) && IsDir (command)) // Remove an existing directory
	command = "rmdir " + command;
      else
	return nil;
      pipeIn = popen (command.c_str (), "r");
      pclose (pipeIn);
      command.clear ();
    }
  return T;
}

dObject *
dTempFile (dList *args)
{
  if (args != 0)
    return new dError ("temp-file requires NO arguments");
  dFile *tmpFile = new dFile ();
  tmpFile->filename = "temp";
  tmpFile->file = tmpfile ();
  return tmpFile;
}

dObject *
dTempName (dList *args)
{
  if (args != 0)
    return new dError ("temp-name requires NO arguments");
  char *ptr = tmpnam (NULL);
  return new dString (ptr);
}

/*
dObject *
dFilePerm (dList *args)
{
}
*/

dObject *
dIsDir (dList *args)
{
  if (args == 0 || args->size != 1 || args->first->car->type != 's')
    return new dError ("is-dir requires a single string parameter");
  string path = ((dString*)args->first->car)->value;
  if (FileExists (path))
    {
      if (IsDir (path))
	return T;
      else
	return nil;
    }
  return new dError ("Could not open file/directory for is-dir");
}

dObject *
dIsFile (dList *args)
{
  if (args == 0 || args->size != 1 || args->first->car->type != 's')
    return new dError ("is-file requires a single string parameter");
  string path = ((dString*)args->first->car)->value;
  if (FileExists (path))
    {
      if (IsFile (path))
	return T;
      else
	return nil;
    }
  path.clear ();
  return new dError ("Could not open file/directory for is-file");
}

dObject *
dFileSize (dList *args)
{
  if (args == 0 || args->first->car->type != 's')
    return new dError ("file-size requires a single string parameter");
  string filename;
  if (checkArgs ("%s", args) == 0)
    {
      filename = ((dString*)((dNode*)args->first)->car)->value;
      int result = isDirectory (filename);
      #ifdef _WIN32
      struct _stat fileInfo;
      if (_stat (filename.c_str (), &fileInfo) != 0)
	return new dError ("is-file could not open the file for checking");
      #else
      struct stat fileInfo;
      if (stat (filename.c_str (), &fileInfo) != 0)
	return new dError ("file-size could not open the file");
      #endif
    }
  return nil;
}

dObject *
dFileExists (dList *args)
{
  if (args == 0 || args->first->car->type != 's')
    return new dError ("path-exists requires a single string parameter");
  if (FileExists (((dString*)args->first->car)->value))
    return T;
  return nil;
}
